package org.springframework.ws.server.endpoint.mapping;

import org.springframework.util.Assert;
import org.springframework.ws.server.endpoint.AbstractDomPayloadEndpoint;
import org.springframework.xml.xpath.proxy.XPathProxyFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Spring-ws PayloadEndpoint implementation that extedns the {@link AbstractDomPayloadEndpoint} to
 * wrap the request dom element with an XPath proxy instance.
 *
 * @author Ray Krueger
 */
public abstract class AbstractXPathProxyPayloadEndpoint<T> extends AbstractDomPayloadEndpoint {

    private final XPathProxyFactory<T> xpathProxyFactory;

    /**
     * Given a target interface an {@link XPathProxyFactory} will be created to produce proxy instances
     *
     * @param targetClass interface annotated with {@link org.springframework.xml.xpath.proxy.XPathMethod} annotations.
     */
    public AbstractXPathProxyPayloadEndpoint(Class<T> targetClass) {
        Assert.notNull(targetClass, "targetClass constructor argument must not be null");
        this.xpathProxyFactory = new XPathProxyFactory<T>(targetClass);
    }

    /**
     * Configure this endpoint to use the given {@link XPathProxyFactory}.
     *
     * @param xpathProxyFactory caller configured {@link XPathProxyFactory}
     */
    public AbstractXPathProxyPayloadEndpoint(XPathProxyFactory<T> xpathProxyFactory) {
        Assert.notNull(xpathProxyFactory, "targetClass constructor argument must not be null");
        this.xpathProxyFactory = xpathProxyFactory;
    }

    protected final Element invokeInternal(Element requestElement, Document responseDocument) throws Exception {
        T proxy = xpathProxyFactory.createProxy(requestElement);
        return invokeWithProxy(proxy, responseDocument);
    }

    /**
     * Subclasses are given the opprotunity to evaluate the XPath proxy instance created by the {@link XPathProxyFactory}
     * for this request.
     *
     * @param proxy            created by the internal {@link XPathProxyFactory} supporting this endpoint instance
     * @param responseDocument DOM document object to use to produce the response.
     * @return responseDocument.getDocumentElement()
     * @throws Exception any exceptions thrown are handled by the framework
     */
    protected abstract Element invokeWithProxy(T proxy, Document responseDocument) throws Exception;
}
